<?php
/**
 * unicode字符解码
 * @param $name
 * @return string
 */
function unicode_decode($name){

    $json = '{"str":"'.$name.'"}';
    $arr = json_decode($json,true);
    if(empty($arr)) return '';
    return $arr['str'];
}
/**
 * @param $length
 * @return string
 * 获取随机的字符串
 */
function getRandChars($length) {
    $str = '';
    $strPol = 'QWERTYUIOPASDFGHJKLXCVBNM1234567890qwertyuiopasdfghjklzxcvbnm';
    $max =strlen($strPol)-1;
    for($i=0;$i<$length;$i++) {
        $str.=$strPol[rand(0,$max)];
    }
    return $str;
}
/**
 * 下划线转驼峰
 * 思路:
 * step1.原字符串转小写,原字符串中的分隔符用空格替换,在字符串开头加上分隔符
 * step2.将字符串中每个单词的首字母转换为大写,再去空格,去字符串首部附加的分隔符.
 */
function camelize($uncamelized_words,$separator='_')
{
    $uncamelized_words = $separator. str_replace($separator, " ", strtolower($uncamelized_words));
    return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), $separator );
}

/**
 * 将下划线转化为首字母大写的类名
 * @param $uncamelized_words
 * @param string $separator
 * @return string
 */
function camelizeClass($uncamelized_words,$separator='_')
{
    $uncamelized_words = str_replace($separator, " ", strtolower($uncamelized_words));
    return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), $separator );
}

/**
 * 驼峰命名转下划线命名
 * 思路:
 * 小写和大写紧挨一起的地方,加上分隔符,然后全部转小写
 */
function uncamelize($camelCaps,$separator='_')
{
    return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . $separator . "$2", $camelCaps));
}
/**
+----------------------------------------------------------
 * 把返回的数据集转换成Tree
+----------------------------------------------------------
 * @access public
+----------------------------------------------------------
 * @param array $list 要转换的数据集
 * @param string $pid parent标记字段
 * @param string $level level标记字段
+----------------------------------------------------------
 * @return array
+----------------------------------------------------------
 */
function listToTree($list, $pk='id',$pid = 'pid',$child = '_child',$root=0) {
    // 创建Tree
    $tree = array();
    if(is_array($list)) {
        // 创建基于主键的数组引用
        $refer = array();
        foreach ($list as $key => $data) {
            $refer[$data[$pk]] =& $list[$key];
        }
        foreach ($list as $key => $data) {
            // 判断是否存在parent
            $parentId = $data[$pid];
            if ($root == $parentId) {
                $tree[] =& $list[$key];
            }else{
                if (isset($refer[$parentId])) {
                    $parent =& $refer[$parentId];
                    $parent[$child][] =& $list[$key];
                }
            }
        }
    }
    return $tree;
}

/**
 * 多维数组的指定多个字段排序
 * @return mixed|null
 * @throws Exception
 */
function sortArrByManyField(){
    $args = func_get_args();
    if(empty($args)){
        return null;
    }
    $arr = array_shift($args);
    if(!is_array($arr)){
        throw new Exception("第一个参数不为数组");
    }
    foreach($args as $key => $field){
        if(is_string($field)){
            $temp = array();
            foreach($arr as $index=> $val){
                $temp[$index] = $val[$field];
            }
            $args[$key] = $temp;
        }
    }
    $args[] = &$arr;//引用值
    call_user_func_array('array_multisort',$args);
    return array_pop($args);
}

/**
 * 判断是否https协议
 * @return bool
 */
function isHttps() {
    if ( !empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') {
        return true;
    } elseif ( isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ) {
        return true;
    } elseif ( !empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) !== 'off') {
        return true;
    }
    return false;
}


/**
 * 检查手机格式
 * @param $phone
 * @return bool
 * author: zhixiao
 */
function checkPhone($phone)
{
    return preg_match("/^1[3456789]\d{9}$/", $phone) ? true : false;
}

/**
 * 多维数组去重
 * @param $arr
 * @param $key
 * @return array
 */
function array_unset($arr,$key){
    //建立一个目标数组
    $res = array();
    foreach ($arr as $value) {
        //查看有没有重复项
        if(isset($res[$value[$key]])){
            //有：销毁
            unset($value[$key]);
        }
        else{
            $res[$value[$key]] = $value;
        }
    }
    return $res;
}

/**
 * 获取ip地址
 *
 * 如果存在NGINX反向代理，需配置：
 * proxy_set_header    Host             $host;#保留代理之前的host
 * proxy_set_header    X-Real-IP        $remote_addr;#保留代理之前的真实客户端ip
 * proxy_set_header    X-Forwarded-For  $proxy_add_x_forwarded_for;
 * proxy_set_header    HTTP_X_FORWARDED_FOR $remote_addr;#在多级代理的情况下，记录每次代理之前的客户端真实ip
 * proxy_redirect      default;#指定修改被代理服务器返回的响应头中的location头域跟refresh头域数值
 */
function getIp() {
    //strcasecmp 比较两个字符，不区分大小写。返回0，>0，<0。
    if (getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
        $ip = getenv('HTTP_CLIENT_IP');
    } elseif (getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
        $ip = getenv('HTTP_X_FORWARDED_FOR');
    } elseif (getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
        $ip = getenv('REMOTE_ADDR');
    } elseif (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
        $ip = $_SERVER['REMOTE_ADDR'];
    }
    $res = preg_match('/[\d\.]{7,15}/', $ip, $matches) ? $matches[0] : '';
    return $res;
}

/**
 * 获取客户端真实IP(公网IP)
 * @return string
 */
function getCustomerIp() {
    $ip = false;
    if(!empty($_SERVER["HTTP_CLIENT_IP"])){
        $ip = $_SERVER["HTTP_CLIENT_IP"];
    }
    if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ips = explode (", ", $_SERVER['HTTP_X_FORWARDED_FOR']);
        if ($ip) { array_unshift($ips, $ip); $ip = false; }
        for ($i = 0; $i < count($ips); $i++) {
            if (!preg_match("/^(10|172.16|192.168)/", $ips[$i])) {
                $ip = $ips[$i];
                break;
            }
        }
    }
    return ($ip ? $ip : $_SERVER['REMOTE_ADDR']);
}

/**打印模型的sql(带参数)
 * @param $model
 * @return mixed|string
 */
function getModelSql($model)
{
    $bindings = $model->getBindings();
    $sql = str_replace('?', '%s', $model->toSql());
    $sql = sprintf($sql, ...$bindings);
    return $sql;
}


/**
 * 经典的概率算法，可用于刮刮卡，大转盘等抽奖算法
 * $proArr是一个预先设置的数组，
 * 假设数组为：array(100,200,300，400)，
 * 开始是从1,1000 这个概率范围内筛选第一个数是否在他的出现概率范围之内，
 * 如果不在，则将概率空间，也就是k的值减去刚刚的那个数字的概率空间，
 * 在本例当中就是减去100，也就是说第二个数是在1，900这个范围内筛选的。
 * 这样 筛选到最终，总会有一个数满足要求。
 * 就相当于去一个箱子里摸东西，
 * 第一个不是，第二个不是，第三个还不是，那最后一个一定是。
 * 这个算法简单，而且效率非常 高，
 * 关键是这个算法已在我们以前的项目中有应用，尤其是大数据量的项目中效率非常棒。
 * 用法参考网址：http://blog.csdn.net/dongsg11200/article/details/24607877
 */
function getPrizeRand($proArr) {
    $result = '';
    //概率数组的总概率精度
    $proSum = array_sum($proArr);
    //概率数组循环
    foreach ($proArr as $key => $proCur) {
        $randNum = mt_rand(1, $proSum);
        if ($randNum <= $proCur) {
            $result = $key;
            break;
        } else {
            $proSum -= $proCur;
        }
    }
    unset ($proArr);
    return $result;
}

/**
 * 获取指定日期段内每一天的日期
 * @param  Date  $startdate 开始日期
 * @param  Date  $enddate   结束日期
 * @return Array
 */
function getDatesFromRange($startdate, $enddate){
    $stimestamp = strtotime($startdate);
    $etimestamp = strtotime($enddate);

    // 计算日期段内有多少天
    $days = ($etimestamp-$stimestamp)/86400+1;
    // 保存每天日期
    $dates = array();
    for($i=0; $i<$days; $i++){
        $dates[] = date('Y-m-d', $stimestamp+(86400*$i));
    }

    return $dates;
}

/**
 * 获取两个日期之间所有日期
 * @param $startDate
 * @param $endDate
 * @return array
 */
function getDatesBetweenTwoDays($startDate, $endDate)
{
    //开始结束日期位置不对，互换数据
    if ($startDate > $endDate) {
        list($startDate, $endDate) = [$endDate, $startDate];
    }
    $dates = [];
    if (strtotime($startDate) > strtotime($endDate)) {
        //如果开始日期大于结束日期，直接return 防止下面的循环出现死循环
        return $dates;
    } elseif ($startDate == $endDate) {
        //开始日期与结束日期是同一天时
        array_push($dates, $startDate);
        return $dates;
    } else {
        array_push($dates, $startDate);
        $currentDate = $startDate;
        do {
            $nextDate = date('Y-m-d', strtotime($currentDate . ' +1 days'));
            array_push($dates, $nextDate);
            $currentDate = $nextDate;
        } while ($endDate != $currentDate);
        return $dates;
    }
}

/**
 * 按日期格式化表格数据方法
 * @param array $originData 原始二位数组数据
 * @param string $dateKey 时间维度key
 * @param array $dataKeys 数据集key
 * @param string $startDate 开始日期
 * @param string $endDate 结束日期
 * @throws Exception
 */
function formatChartDataByDate($originData = [], $dateKey = '', $dataKeys = [], $startDate = '', $endDate = '')
{
    //先按日期排序
    $sortByLabelKey = sortArrByManyField($originData, $dateKey, SORT_ASC);
    //以日期key重组数据
    $dateKeyGroupList = [];
    if (!empty($sortByLabelKey)) {
        foreach ($sortByLabelKey as $item) {
            if (isset($item[$dateKey])) {
                $dateKeyGroupList[$item[$dateKey]] = $item;
            }
        }
    }
    //时间维度列表
    $startDate || $startDate = date('Y-m-d', strtotime('-7 days'));
    $endDate || $endDate = date('Y-m-d', strtotime('-1 days'));
    $dateList = getDatesBetweenTwoDays($startDate, $endDate);
    //检索原始数据并合成格式化数据
    $dataSet = [];
    foreach ($dateList as $dateVal) {
        foreach ($dataKeys as $fieldKey) {
            isset($dataSet[$fieldKey]) || $dataSet[$fieldKey] = [];
            if (isset($dateKeyGroupList[$dateVal][$fieldKey])) {
                $dataSet[$fieldKey][] = (float)$dateKeyGroupList[$dateVal][$fieldKey];
            } else {
                $dataSet[$fieldKey][] = 0;
            }
        }
    }
    return ['dateList' => $dateList, 'dateSet' => $dataSet];
}

/**
 * 按小时格式化表格数据方法
 * @param array $originData 原始二位数组数据
 * @param string $dateKey 时间维度key
 * @param array $dataKeys 数据集key
 * @throws Exception
 */
function formatChartDataByHour($originData = [], $dateKey = '', $dataKeys = [])
{
    //先按日期排序
    $sortByLabelKey = sortArrByManyField($originData, $dateKey, SORT_ASC);
    //以日期key重组数据
    $dateKeyGroupList = [];
    if (!empty($sortByLabelKey)) {
        foreach ($sortByLabelKey as $item) {
            if (isset($item[$dateKey])) {
                $dateKeyGroupList[$item[$dateKey]] = $item;
            }
        }
    }
    //时间维度列表
    $hourList = [];
    for ($i = 0; $i <= 2300; $i += 100) {
        $hourList[] = $i;
    }
    //检索原始数据并合成格式化数据
    $dataSet = [];
    foreach ($hourList as $dateVal) {
        foreach ($dataKeys as $fieldKey) {
            isset($dataSet[$fieldKey]) || $dataSet[$fieldKey] = [];
            if (isset($dateKeyGroupList[$dateVal][$fieldKey])) {
                $dataSet[$fieldKey][] = (float)$dateKeyGroupList[$dateVal][$fieldKey];
            } else {
                $dataSet[$fieldKey][] = 0;
            }
        }
    }
    //转换时间格式
    $hourList = array_map(function ($hourNum) {
        $hourNum = str_pad($hourNum, 4, '0', STR_PAD_LEFT);
        return date('H:i', strtotime($hourNum));
    }, $hourList);
    return ['dateList' => $hourList, 'dateSet' => $dataSet];
}

/**
 * 获取指定年份所有周列表
 * @param $year
 */
function getWeeksByYear($year = '')
{
    $year || $year = date('Y');
    //获取当年首个周一所在日期
    $yearStart = $year . '-01-01';
    do {
        $weekNum = date('w', strtotime($yearStart));
        $firstWeekDay = $yearStart;
        $yearStart = date('Y-m-d', strtotime($yearStart . ' +1 days'));
    } while ((int)$weekNum != 1);
    //收集所有周一列表
    $weekList = [];
    do {
        $weekList[] = $firstWeekDay;
        $firstWeekDay = date('Y-m-d', strtotime($firstWeekDay . ' +1 week'));
    } while (date('Y', strtotime($firstWeekDay)) == $year && strtotime($firstWeekDay) <= time());
    return $weekList;
}

/**
 * 按周格式化表格数据方法
 * @param array $originData 原始二位数组数据
 * @param string $dateKey 时间维度key
 * @param array $dataKeys 数据集key
 * @param string $year 年份
 * @return array
 * @throws Exception
 */
function formatChartDataByWeek($originData = [], $dateKey = '', $dataKeys = [], $year = '')
{
    //先按日期排序
    $sortByLabelKey = sortArrByManyField($originData, $dateKey, SORT_ASC);
    //以日期key重组数据
    $dateKeyGroupList = [];
    if (!empty($sortByLabelKey)) {
        foreach ($sortByLabelKey as $item) {
            if (isset($item[$dateKey])) {
                $dateKeyGroupList[$item[$dateKey]] = $item;
            }
        }
    }
    //时间维度列表
    $weekList = getWeeksByYear($year);
    //检索原始数据并合成格式化数据
    $dataSet = [];
    foreach ($weekList as $dateVal) {
        foreach ($dataKeys as $fieldKey) {
            isset($dataSet[$fieldKey]) || $dataSet[$fieldKey] = [];
            if (isset($dateKeyGroupList[$dateVal][$fieldKey])) {
                $dataSet[$fieldKey][] = (float)$dateKeyGroupList[$dateVal][$fieldKey];
            } else {
                $dataSet[$fieldKey][] = 0;
            }
        }
    }
    return ['dateList' => $weekList, 'dateSet' => $dataSet];
}

/**
 * 获取指定年份所有月列表
 * @param $year
 */
function getMonthsByYear($year = '')
{
    $year || $year = date('Y');
    //获取当年shou周一所在日期
    $yearStart = $year . '-01-01';
    //收集所有周一列表
    $dayList = [];
    do {
        $dayList[] = $yearStart;
        $yearStart = date('Y-m-d', strtotime($yearStart . ' +1 month'));
    } while (date('Y', strtotime($yearStart)) == $year && strtotime($yearStart) <= time());
    return $dayList;
}

/**
 * 按月格式化表格数据方法
 * @param array $originData 原始二位数组数据
 * @param string $dateKey 时间维度key
 * @param array $dataKeys 数据集key
 * @param string $year 年份
 * @return array
 * @throws Exception
 */
function formatChartDataByMonth($originData = [], $dateKey = '', $dataKeys = [], $year = '')
{
    //先按日期排序
    $sortByLabelKey = sortArrByManyField($originData, $dateKey, SORT_ASC);
    //以日期key重组数据
    $dateKeyGroupList = [];
    if (!empty($sortByLabelKey)) {
        foreach ($sortByLabelKey as $item) {
            if (isset($item[$dateKey])) {
                $dateKeyGroupList[$item[$dateKey]] = $item;
            }
        }
    }
    //时间维度列表
    $monthList = getMonthsByYear($year);
    //检索原始数据并合成格式化数据
    $dataSet = [];
    foreach ($monthList as $dateVal) {
        foreach ($dataKeys as $fieldKey) {
            isset($dataSet[$fieldKey]) || $dataSet[$fieldKey] = [];
            if (isset($dateKeyGroupList[$dateVal][$fieldKey])) {
                $dataSet[$fieldKey][] = (float)$dateKeyGroupList[$dateVal][$fieldKey];
            } else {
                $dataSet[$fieldKey][] = 0;
            }
        }
    }
    //转换时间格式
    $monthList = array_map(function ($monthStr) {
        return date('Y-m', strtotime($monthStr));
    }, $monthList);
    return ['dateList' => $monthList, 'dateSet' => $dataSet];
}

/**
 * 返回一个顶级的域名
 * 例：sub.a.com.cn 返回 a.com.cn
 * @param string $url
 * @return mixed|string
 */
function getTopDomain($url = '')
{
    $host = strtolower(urldecode($url));
    if (strpos($host, '/') !== false) {
        $parse = parse_url($host);
        $host  = $parse['host'];
    }
    $topleveldomaindb = ['com', 'cn', 'net', 'com.cn', 'net.cn'];
    $str = '';
    foreach ($topleveldomaindb as $v) {
        $str .= ($str ? '|' : '') . $v;
    }
    $matchstr = "[^\.]+\.(?:(" . $str . ")|\w{2}|((" . $str . ")\.\w{2}))$";
    if (preg_match("/" . $matchstr . "/ies", $host, $matchs)) {
        $domain = $matchs['0'];
    } else {
        $domain = $host;
    }
    return $domain;
}

/**
 * 获取随机字符串
 * @param $length
 * @return string
 */
function random_str($length)
{
    //生成一个包含 大写英文字母, 小写英文字母, 数字 的数组
    $arr = array_merge(range(0, 9), range('a', 'z'), range('A', 'Z'));

    $str = '';
    $arr_len = count($arr);
    for ($i = 0; $i < $length; $i++)
    {
        $rand = mt_rand(0, $arr_len-1);
        $str.=$arr[$rand];
    }

    return $str;
}

/**
 * 身份证号码验证
 * site http://bbs.it-home.org
 * create by Aston 2019-10-27
 */
function checkIdCard($idcard){
    if(empty($idcard)){
        return false;
    }
    $City = array(11=>"北京",12=>"天津",13=>"河北",14=>"山西",15=>"内蒙古",21=>"辽宁",22=>"吉林",23=>"黑龙江",31=>"上海",

        32=>"江苏",33=>"浙江",34=>"安徽",35=>"福建",36=>"江西",37=>"山东",41=>"河南",42=>"湖北",43=>"湖南",44=>"广东",45=>"广西",

        46=>"海南",50=>"重庆",51=>"四川",52=>"贵州",53=>"云南",54=>"西藏",61=>"陕西",62=>"甘肃",63=>"青海",64=>"宁夏",65=>"新疆",

        71=>"台湾",81=>"香港",82=>"澳门",91=>"国外");
    $iSum = 0;
    $idCardLength = strlen($idcard);
    //长度验证
    if(!preg_match('/^\d{17}(\d|x)$/i',$idcard) and!preg_match('/^\d{15}$/i',$idcard)) {
        return false;
    }
    //地区验证
    if(!array_key_exists(intval(substr($idcard,0,2)),$City)) {
        return false;
    }
    // 15位身份证验证生日，转换为18位
    if ($idCardLength == 15) {
        $sBirthday = '19'.substr($idcard,6,2).'-'.substr($idcard,8,2).'-'.substr($idcard,10,2);
        $d = new DateTime($sBirthday);
        $dd = $d->format('Y-m-d');
        if($sBirthday != $dd) {
            return false;
        }
        $idcard = substr($idcard,0,6)."19".substr($idcard,6,9);//15to18
        $Bit18 = getVerifyBit($idcard);//算出第18位校验码
        $idcard = $idcard.$Bit18;
    }
    // 判断是否大于2078年，小于1900年
    $year = substr($idcard,6,4);
    if ($year<1900 || $year>2078 ) {
        return false;
    }
    //18位身份证处理
    $sBirthday = substr($idcard,6,4).'-'.substr($idcard,10,2).'-'.substr($idcard,12,2);
    $d = new DateTime($sBirthday);
    $dd = $d->format('Y-m-d');
    if($sBirthday != $dd) {
        return false;
    }
    //身份证编码规范验证
    $idcard_base = substr($idcard,0,17);
    if(strtoupper(substr($idcard,17,1)) != getVerifyBit($idcard_base)) {
        return false;
    }else{
        return true;
    }
}
// 计算身份证校验码，根据国家标准GB 11643-1999
function getVerifyBit($idcard_base){
    if(strlen($idcard_base) != 17) {
         return false;
    }
    //加权因子
    $factor = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);
    //校验码对应值
    $verify_number_list = array('1', '0', 'X', '9', '8', '7', '6', '5', '4','3', '2');
    $checksum = 0;
    for ($i = 0; $i < strlen($idcard_base); $i++) {
        $checksum += substr($idcard_base, $i, 1) * $factor[$i];
    }
    $mod = $checksum % 11;
    $verify_number = $verify_number_list[$mod];
    return $verify_number;
}
